#include "GameLight.h"

bool GameLight::mShowDebug = false;

GameLight::GameLight() : GameObject("GameLight"+StringConverter::toString(howMany())),
	mLight(0),
	mCamFacingNode(0),
	mDebugNode(0)
{
}

//GameLight::GameLight(SceneManager* sceneMgr) : BaseGameObject(sceneMgr)
//{
//}

//GameLight::GameLight(String name, SceneNode* node) : BaseGameObject(name, node)
//{
//	mNode->setUserAny(Any(static_cast<BaseGameObject*>(this)));
//
//	onMoved.connect(boost::bind(&GameLight::update, this));
//	onRotated.connect(boost::bind(&GameLight::update, this));
//	onSelected.connect(boost::bind(&GameLight::setShowRange, this, true));
//	onDeselected.connect(boost::bind(&GameLight::setShowRange, this, false));
//}

GameLight::~GameLight()
{
	Ogre::Root::getSingleton().removeFrameListener(this);
}

bool GameLight::frameEnded(const Ogre::FrameEvent& evt)
{
	return true;
}

bool GameLight::frameStarted(const Ogre::FrameEvent& evt)
{
//	if(mNode->numChildren()==0) return true;
	
	mResizingNode->setVisible(GameLight::mShowDebug);
	mDebugNode->setVisible(GameLight::mShowDebug);
	mRangeNode->setVisible(GameLight::mShowDebug);


	if(!mLight || mLight->getType()!=Light::LT_POINT) return true;
	Camera* cam = App::getSingletonPtr()->getCamera();
	if(!cam) return true;
	Vector3 dir = cam->getDerivedDirection();

	if(mCamFacingNode)
		mCamFacingNode->setDirection(dir, Node::TS_WORLD);

	Vector3 camPos = cam->getDerivedPosition();
//	Vector3 v = camPos - mNode->getPosition();
//	if(v.isNaN()) return true;
	
//	Real d = v.length() / 100.f;
//	if(Math::isNaN(d)) return true;
	
//	mResizingNode->setScale(d,d,d);

	return true;
}

void GameLight::createVisual()
{
//	ProceduralPrimitiveFactory ppf(mSceneMgr);
	SceneManager* sm = Ogre::Root::getSingleton().getSceneManagerIterator().getNext();
	ProceduralPrimitiveFactory ppf(sm);
	Ogre::String idStr = StringConverter::toString(mId);
	Ogre::String entName = "LightSphereEntity"+idStr;
	ppf.createSphere(entName, "VertexColour/Flat/NoCull", mLight->getDiffuseColour(), 1, 16, 16, true, 1, 1, 1);

		
	mDebugNode = sm->getRootSceneNode()->createChildSceneNode("DebugNode"+idStr);
	mDebugNode->setPosition(mNode->getPosition());

	mResizingNode = sm->getRootSceneNode()->createChildSceneNode("ResizingNode"+idStr);
	mResizingNode->setPosition(mNode->getPosition());
	
//	mRangeNode = sm->getRootSceneNode()->createChildSceneNode("RangeNode"+idStr);
	mRangeNode = mNode->createChildSceneNode("RangeNode"+idStr);
	mRangeNode->setVisible(false, true);

//	mEntity = mSceneMgr->createEntity(entName);
	mEntity = sm->createEntity(entName);
	mEntity->setCastShadows(false);
	
	UserObjectBindings uob = mEntity->getUserObjectBindings();
	uob.setUserAny(Any(dynamic_cast<BaseGameObject*>(this)));
	mEntity->setUserAny(Any(dynamic_cast<BaseGameObject*>(this)));

	Ogre::LogManager::getSingletonPtr()->logMessage("[GameLight]" + mName + "\t:" +	StringConverter::toString(mLight->getDiffuseColour()));


	mMaterial = Ogre::MaterialManager::getSingleton().create(getName() + "_Material", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
	mMaterial->setReceiveShadows(false);

	mMaterial->getTechnique(0)->setSchemeName("GBuffer");
	mMaterial->getTechnique(0)->getPass(0)->setLightingEnabled(false);
//	mMaterial->getTechnique(0)->getPass(0)->setDiffuse(mLight->getDiffuseColour());
	mMaterial->getTechnique(0)->getPass(0)->setDiffuse(this->getProperty<ColourValue>("diffuse"));
//			mMaterial->getTechnique(0)->getPass(0)->setVertexColourTracking(Ogre::TVC_DIFFUSE);
	//Ogre::Material::
	mMaterial->createTechnique();
	mMaterial->getTechnique(1)->setSchemeName("NoGBuffer");
	mMaterial->getTechnique(1)->createPass();
	mMaterial->getTechnique(1)->getPass(0)->setLightingEnabled(false);
//	mMaterial->getTechnique(1)->getPass(0)->setDiffuse(mLight->getDiffuseColour());
	mMaterial->getTechnique(1)->getPass(0)->setDiffuse(this->getProperty<ColourValue>("diffuse"));
//			mMaterial->getTechnique(1)->getPass(0)->setVertexColourTracking(Ogre::TVC_DIFFUSE);

	if (mLight->getType()==Ogre::Light::LT_POINT) drawPointLight();
	if (mLight->getType()==Ogre::Light::LT_DIRECTIONAL) drawDirectionalLight();
	if (mLight->getType()==Ogre::Light::LT_SPOTLIGHT) drawSpotLight();

	mResizingNode->attachObject(mEntity);

	mHeightLine =  sm->createManualObject("HeightLine"+idStr);
	mHeightLine->setDynamic(true);
	mHeightLine->setCastShadows(false);
	mHeightLine->estimateVertexCount(2);
	mHeightLine->estimateIndexCount(2);

//	ColourValue col = isSelected() ? ColourValue(1,1,1,.5) : ColourValue(.3,.3,.3,.3);
	ColourValue col = ColourValue(.3,.3,.3,.3);

	mHeightLine->begin("FlatVertexColourAlphaBlended", Ogre::RenderOperation::OT_LINE_LIST);
	mHeightLine->position(0,0,0);
	mHeightLine->colour(col);
	mHeightLine->position(0,-mNode->getPosition().y,0);
	mHeightLine->colour(col);
	mHeightLine->end();

	mDebugNode->attachObject(mHeightLine);
}


void GameLight::drawPointLight()
{
	SceneManager* sm = Ogre::Root::getSingleton().getSceneManagerIterator().getNext();
	
	Ogre::String circleName = "circle_name"+mId;
	ManualObject * circle = sm->createManualObject(circleName);
//    float const radius = 7;
	float const radius = mLight->getAttenuationRange();

	// accuracy is the count of points (and lines).
	// Higher values make the circle smoother, but may slowdown the performance.
	// The performance also is related to the count of circles.
	float const accuracy = 35;

	circle->begin(mMaterial->getName(), RenderOperation::OT_LINE_STRIP);

	unsigned point_index = 0;
	for (float theta = 0; theta <= 2 * Math::PI; theta += Math::PI / accuracy)
		{
			circle->position(radius * cos(theta), 0, radius * sin(theta));
			circle->colour(mLight->getDiffuseColour());
			circle->index(point_index++);
		}
	circle->index(0); // Rejoins the last point to the first.

	circle->end();
	circle->setCastShadows(false);
	circle->setVisible(false);
	mRangeNode->attachObject(circle);

	Ogre::String circle2Name = "circle2_name"+mId;
	ManualObject * circle2 = sm->createManualObject(circle2Name);


	circle2->begin(mMaterial->getName(), RenderOperation::OT_LINE_STRIP);

	point_index = 0;
	for (float theta = 0; theta <= 2 * Math::PI; theta += Math::PI / accuracy)
		{
			circle2->position(radius * cos(theta), radius * sin(theta), 0);
			circle2->colour(mLight->getDiffuseColour());
			circle2->index(point_index++);
		}
	circle2->index(0); // Rejoins the last point to the first.

	circle2->end();
	circle2->setCastShadows(false);
	circle2->setVisible(false);
	mRangeNode->attachObject(circle2);


	Ogre::String circle3Name = "circle3_name"+mId;
	ManualObject * circle3 = sm->createManualObject(circle3Name);
	circle3->begin(mMaterial->getName(), RenderOperation::OT_LINE_STRIP);
	point_index = 0;
	for (float theta = 0; theta <= 2 * Math::PI; theta += Math::PI / accuracy)
		{
			circle3->position(0, radius * cos(theta), radius * sin(theta));
			circle3->colour(mLight->getDiffuseColour());
			circle3->index(point_index++);
		}
	circle3->index(0); // Rejoins the last point to the first.
	circle3->end();
	circle3->setCastShadows(false);
	circle3->setVisible(false);
	mRangeNode->attachObject(circle3);



	mCamFacingNode = mDebugNode->createChildSceneNode("circle4_node"+mId);
	Ogre::String circle4Name = "circle4_name"+mId;
	ManualObject * circle4 = sm->createManualObject(circle4Name);
	circle4->begin("FlatVertexColourAdd", RenderOperation::OT_LINE_STRIP);
	point_index = 0;
//	for (float theta = 0; theta <= 2 * Math::PI; theta += Math::PI / accuracy)
	float step = Math::PI / accuracy / 8;
	for (float theta = 0; theta <= 2 * Math::PI; theta += step)
		{
			circle4->position(radius * cos(theta), radius * sin(theta), 0);
			circle4->colour(ColourValue::White);
			circle4->index(point_index++);

			theta += step;
			circle4->position(radius * cos(theta), radius * sin(theta), 0);
			circle4->colour(ColourValue(0,0,0,0));
			circle4->index(point_index++);

			theta += step;
			circle4->position(radius * cos(theta), radius * sin(theta), 0);
			circle4->colour(ColourValue(0,0,0,0));
			circle4->index(point_index++);
		}
	circle4->index(0); // Rejoins the last point to the first.
	circle4->end();
	circle4->setCastShadows(false);
	mCamFacingNode->attachObject(circle4);
//	mCamFacingNode->setVisible(false);
}


void GameLight::drawDirectionalLight()
{
	SceneManager* sm = Ogre::Root::getSingleton().getSceneManagerIterator().getNext();
	assert(sm);

	Vector3 dir = mLight->getDirection();
	Vector3 n = dir*10000;

	ManualObject* mo =  sm->createManualObject("manual"+mId);
//	SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode("manual_node"+mId);
	mo->setCastShadows(false);

	mo->begin(mMaterial->getName(), Ogre::RenderOperation::OT_LINE_LIST);
	mo->position(0,0,0);
	mo->colour(mLight->getDiffuseColour());
	mo->position(n);
	mo->colour(mLight->getDiffuseColour());
	mo->end();

	mRangeNode->attachObject(mo);
}

void GameLight::drawSpotLight()
{
//	SceneManager* sm = Ogre::Root::getSingleton().getSceneManagerIterator().getNext();
//
//	ManualObject* mo =  sm->createManualObject("manual"+mId);
////	SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode("manual_node"+mId);
////	SceneNode* node = mNode->createChildSceneNode("manual_node"+mId);
//	mo->setCastShadows(false);
//
//	Vector3 dir = mLight->getDirection();
//	Real dist = mNode->getPosition().length();
//	Vector3 n = dir*dist;
//	float radius = dist*mLight->getSpotlightOuterAngle().valueRadians();
//
//	mo->begin(mMaterial->getName(), Ogre::RenderOperation::OT_LINE_LIST);
//	mo->position(0,0,0);
//	mo->colour(mLight->getSpecularColour());
//	mo->position(n.x,n.y,n.z);
//	mo->colour(mLight->getSpecularColour());
//
//	for (float theta = 0; theta <= 2 * Math::PI; theta += Math::PI / 2)
//		{
//			mo->position(0,0,0);
//			mo->colour(mLight->getDiffuseColour());
//			Vector3 v(radius * cos(theta), 0, radius * sin(theta));
//			Quaternion q = Vector3::UNIT_Y.getRotationTo(dir);
//			v = q*v + n;
//			mo->position(v.x,v.y,v.z);
//			mo->colour(mLight->getDiffuseColour());
//		}
//
//	mo->end();
//
//	node->attachObject(mo);
//	mo->setVisible(false);
//	mRangeNode->attachObject(mo);
//
//	String circleName = "circle_name"+mId;
//	ManualObject * circle = mSceneMgr->createManualObject(circleName);
//	float const accuracy = 35;
//	circle->begin(mMaterial->getName(), RenderOperation::OT_LINE_STRIP);
//	unsigned point_index = 0;
//	for (float theta = 0; theta <= 2 * Math::PI; theta += Math::PI / accuracy)
//		{
//			Vector3 v(radius * cos(theta), 0, radius * sin(theta));
//			Quaternion q = Vector3::UNIT_Y.getRotationTo(dir);
//			v = q*v + n;
//			circle->position(v.x,v.y,v.z);
//			circle->colour(mLight->getDiffuseColour());
//			circle->index(point_index++);
//		}
//	circle->index(0);
//	circle->end();
//	circle->setVisible(false);
//	mRangeNode->attachObject(circle);
//
//	String circle2Name = "circle2_name"+mId;
//	ManualObject * circle2 = mSceneMgr->createManualObject(circle2Name);
//	radius = dist*mLight->getSpotlightInnerAngle().valueRadians();
//	circle2->begin(mMaterial->getName(), RenderOperation::OT_LINE_STRIP);
//	point_index = 0;
//	for (float theta = 0; theta <= 2 * Math::PI; theta += Math::PI / accuracy)
//		{
//			Vector3 v(radius * cos(theta), 0, radius * sin(theta));
//			Quaternion q = Vector3::UNIT_Y.getRotationTo(dir);
//			v = q*v + n;
//			circle2->position(v.x,v.y,v.z);
//			circle2->colour(mLight->getDiffuseColour());
//			circle2->index(point_index++);
//		}
//	circle2->index(0);
//	circle2->end();
//	circle2->setVisible(false);
//	mRangeNode->attachObject(circle2);
}

void GameLight::update()
{
	Object trans = this->getProperty<Object>("transform");
	Vector3 p = trans.getProperty<Vector3>("pos") ;
	
//	Vector3 p = mResizingNode->getPosition();
	mNode->setPosition(p);
	mDebugNode->setPosition(p);

	Quaternion q = mResizingNode->getOrientation();
	mNode->setOrientation(q);
	mDebugNode->setOrientation(q);

	if(mLight->getType()==Light::LT_POINT)
		mRangeNode->setOrientation(q.Inverse());

	mHeightLine->beginUpdate(0);
	mHeightLine->position(0,0,0);
	mHeightLine->colour(ColourValue(1,1,1,.75));
	mHeightLine->position(0,-p.y,0);
	mHeightLine->colour(ColourValue(1,1,1,.75));
	mHeightLine->end();
}

void GameLight::setShowRange(bool b)
{
	mRangeNode->setVisible(b);
//	mCamFacingNode->setVisible(!b);
	update();
}

void GameLight::init()
{
	GameObject::init();
	assert(mLight);
	
	Ogre::Root::getSingleton().addFrameListener(this);
	
	Object trans = getProperty<Object>("transform");
	
	mNode = getProperty<SceneNode*>("scenenode");
	Vector3 p = mNode->getPosition();
	prop_cast<Vector3>(trans["pos"])->Set(p);
	
	this->addProperty<Entity*>("entity", mEntity);
	
	this->addProperty<ColourValue>("diffuse", mLight->getDiffuseColour());
	this->addProperty<ColourValue>("specular", mLight->getSpecularColour());

	createVisual();
}
